// 本代码是用 C/C++ 连接数据库
// 用该库需要先安装 ,  然后在系统路径 /usr/include 下找 mysql/mysql.h 这是声明

#include <iostream>
#include <string>
#include <mysql/mysql.h>

const std::string host = "localhost";
const std::string usr = "GJG";
const std::string password = "123456";
const std::string db = "connector";
const unsigned int port = 3306;

int main()
{

    // 1. 先初始化数据库
    MYSQL sq;
    MYSQL *sql = mysql_init(&sq); // 初始化失败返回 nullptr
    if (sql == nullptr)
    {
        std::cout << "mysql init error !" << std::endl;
        return 1;
    }

    std::cout << "mysql init success !" << std::endl;

    // 2. 连接想要连接的数据库 -- 所以前提是我们要先创建出来该库
    // 第一个参数是 Init 返回值
    MYSQL *msql = mysql_real_connect(sql, host.c_str(), usr.c_str(), password.c_str(),
                                     db.c_str(), port, nullptr, 0);
    if (msql == nullptr)
    {
        std::cout << "mysql connect error " << std::endl;
        return 2;
    }

    std::cout << "mysql connect success ... " << std::endl;

    // 3. 设置字符规则 , 符合中文
    mysql_set_character_set(msql, "utf8");

    // 4. 连接成功就可以执行了 , 可以执行增删查改 ----- 但是查比较麻烦
    // 所有执行sql语句的就用  mysql_query();

    // 增
    const std::string my_sql = "insert into info values(11,'张三',12324,1)";
    const std::string my_sql1 = "insert into info values(20,'王五',32455,2)";
    const std::string my_sql2 = "insert into info values(14,'李四',27677,3)";
    const std::string my_sql3 = "insert into info values(24,'翠花',08457,2)";

    // 删
    const std::string my_sql5 = "delete from info where name='张三'";

    // 改
    const std::string my_sql6 = "update info set id=53555 where name='王五'";

    // 查
    const std::string my_sql7 = "select * from info";

    // 增

    // mysql_query(msql , my_sql.c_str());
    // mysql_query(msql , my_sql1.c_str());
    // mysql_query(msql , my_sql2.c_str());
    // mysql_query(msql , my_sql3.c_str());

    // 删
    // mysql_query(msql, my_sql5.c_str());

    // // 改
    // mysql_query(msql, my_sql6.c_str());

    // *********** 重点是查 --- select 是最麻烦的 , 因为查我们需要显示给我们
    // 1. 我们执行的sql 都是事务 , 并且都会真正写到数据库中
    // 2. 我们需要从数据库获取内容, 并显示给我们 , 所以 :
    // 需要我们使用 MYSQL_RES *mysql_store_result(MYSQL *mysql) 函数将内容先获取到 MYSQL_RES 这个结果结构体中
    // 我们写的命令等本质就是字符串 - char* , 所以我们可以理解 这个结构体就是一个 char** 存了各种一维char*数组

    // 查
    mysql_query(msql, my_sql7.c_str());

    // 获取存储结果 , 存的是所有信息
    MYSQL_RES *res = mysql_store_result(msql);
    if (res == nullptr)
        return 3;

    // MYSQL_RES 中获取的是全部内容 , 但是我们想要获取列内容 , 行内容就要用其它函数

    // 获取MYSQL_RES 中的行的个数
    int rows = mysql_num_rows(res);
    // 获取MYSQL_RES 中的列的个数
    int list = mysql_num_fields(res);

    // 所以 , 有了行和列我就可以使用 ----- MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)
    //  获取内容了 , 该函数返回的是一个内容 , 但这个内容中会包含很多 char* 比如 '张三' '2323' 等 ..

    // 1. 显示出列名信息属性   -- MYSQL_FIELD 这是一个结构体 
    MYSQL_FIELD *fieldname = mysql_fetch_fields(res);
    // 遍历列
    for (int i = 0; i < list; ++i)
    {
        std::cout << fieldname[i].name << " ";
    }
    std::cout << std::endl;

    // 2 . 所以我们想要显示出所有的内容 , 就要像遍历二维数组一样
    MYSQL_ROW line;
    int i = 0;
    for (i; i < rows; ++i)
    {
        // 先获取一行内容
        line = mysql_fetch_row(res);
        // 再获取一行中的每一个内容
        for (int j = 0; j < list; ++j)
        {
            std::cout << line[j] << " ";
        }
        std::cout << std::endl;
    }

    // 最后一定要释放结果集 -- 防止内存泄漏
    mysql_free_result(res);

    mysql_close(msql);
    std::cout << "mysql close success ... " << std::endl;

    return 0;
}


///////////////////////// 写在最后的总结 : 

#if 0 
    用语言连接数据库  -- C/C++ 

    // 0. 我们已经创建好了该数据库 , 并且赋予该用户相关权限 , 并且也有CURD 相关的表 
    // 1. 初始化数据库  mysql_init 
    // 2. 连接数据库  --- mysql_real_connect 
    // 3. 若想支持中文 , 需用  mysql_set_character_set() 设置为 "utf8" 
    // 4. 连接成功后 , 可以正常使用了 : 使用 sql 语句 ---- mysql_query 

    // 5. 若涉及查 --- 比较复杂 -- 必须先获取内容 --- stroe
                                  然后使获取行 , 列等看需求 , 打印

    // 6. 最后一定一定一定要 free 结果集  --- mysql_free_result 
    // 7. 关闭数据库 


#endif 